home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr52 / rntx.zip / REINDEX.PRG < prev   
Text File  |  1993-04-01  |  20KB  |  657 lines

  1. ***********
  2. *
  3. *   Program....:  REINDEX.PRG
  4. *   Author.....:  Reggie Moseley
  5. *   Date.......:  02/02/90
  6. *   Purpose....:  Creates/Rebuilds NTX indexes for DBF files by reading
  7. *                 filenames and key expressions from an ASCII file.
  8. *   Usage.....:   REINDEX [infile.[ext]] [alt_delm]                             
  9. *   Link......:   LINK reindex,,nul,clipper+extend /SE:512 /NOE
  10. *                 PLINK86 fi reindex lib clipper, extend                       
  11. *   Parameters:   infile.ext - name of ASCII file containing the file and
  12. *                 index key data. The format for each line of data is:
  13. *
  14. *                       dbfname # ntxname # key_expression
  15. *
  16. *                 Where '#' is the delimiter character separating the values,
  17. *                 'dbfname' is the name of the DBF, 'ntxname' is the name of
  18. *                 the NTX file, and 'key_expression' is a valid Clipper
  19. *                 index expression, which may contain Clipper and Extend.LIB
  20. *                 functions. If 'infile' is not passed, the program searches
  21. *                 for REINDEX.KEY to use as the default input file. If infile
  22. *                 is passed without an extension (.ext), the default is KEY.
  23. *                 An '*' can be used to comment out lines in the input file.
  24. *
  25. *                 alt_delm - character to use as a delimiter instead of '#'.
  26. *                 It must be one character in length and may not be an alpha-
  27. *                 betic, nor numeric, nor low ASCII (hex code less than 20),
  28. *                 nor high ASCII (hex code greater than 7F).To assign a space
  29. *                 as the delimiter, pass an '*' (asterik may not be assigned
  30. *                 as a delimiter). The delimiter may be reassigned in the 
  31. *                 input text file by inserting the following comment line:
  32. *
  33. *                       * delimiter alt_delm
  34. *
  35. *                 The new delimiter remains in effect until it is changed by
  36. *                 another delimiter comment. The aforementioned rules for
  37. *                 delimiter assignment apply to 'alt_delm' in the delimiter
  38. *                 comment. If the comment appears without an alt_delm, the
  39. *                 delimiter will revert back to the original '#' character.
  40. *
  41.    parameters textname, new_delm
  42.  
  43.    private size, buffer, thandle, more_text, temp_bufr, more_chrs, delm_code
  44.    public inputname, inputfile
  45.  
  46.    if type( "textname" ) = "U"           && no parms passed?
  47.       inputname = "REINDEX.KEY"          && use default input
  48.    else
  49.       inputname = textname
  50.    endif
  51.  
  52.    if "?" $ inputname                    && ? means display help
  53.       usage_help()
  54.    endif
  55.  
  56.    inputfile = val_fname( inputname, "KEY" ) 
  57.    inputname = val_fname( inputname )
  58.  
  59.    if !file( inputfile )                 && no input file found?
  60.       if inputfile $ "REINDEX.KEY"       && if no parms passed, display help
  61.          usage_help()
  62.       else                               && else display an error message
  63.          ? "File " + inputfile + " not found in current directory"
  64.          ? "DOS Errorlevel: 2"
  65.          errorlevel( 2 )                 && for use within batch files
  66.          quit
  67.       endif
  68.    endif
  69.  
  70.    public delm
  71.    delm = if( type( "new_delm" ) == "C", val_delim( new_delm ), "#" )
  72.  
  73.    binit( 1, 2048 )                      && initialize 2K buffer
  74.  
  75.    thandle = bopen( inputfile, 0 )       && open text file read only
  76.  
  77.    if ferror() != 0                      && text file not opened 
  78.       ? "Open error on file " + inputfile
  79.       ? "DOS Errorlevel: 1"
  80.       errorlevel( 1 )
  81.       quit
  82.    endif
  83.  
  84.    size   = 80
  85.    buffer = space( size )                && init buffer to max line size
  86.  
  87.    ? "REINDEX - Clipper Reindexing Utility, Version 1.0"
  88.    ? "Written by Reggie Moseley, Regmo Systems, Copyright (c) 1990."
  89.    ?
  90.  
  91.    select 0
  92.    more_text = bread( thandle, @buffer, size )       && prime the buffer
  93.  
  94.    do while more_text != 0
  95.       more_chrs = scanlines( @buffer )   && do each line in buffer
  96.       if more_chrs = 0
  97.          more_text = bread( thandle, @buffer, size ) && refill the buffer
  98.       else
  99.          temp_bufr = buffer
  100.          more_text = bread( thandle, @buffer, size ) && append chars to
  101.          buffer = temp_bufr + buffer                 && current buffer
  102.       endif
  103.    enddo
  104.  
  105.    ?
  106.    ? "All files declared in " + inputfile + ;
  107.      " have been successfully reindexed"
  108.    ?
  109.  
  110.    bclose( thandle )                     && close text file
  111.  
  112.    bend()                                && terminate buffer system
  113.  
  114.    errorlevel( 0 )                       && let batch file know we're OK
  115.  
  116. quit
  117.  
  118.  
  119. external strtran, alltrim, left, rat, right, descend, examplep
  120.  
  121.  
  122.  
  123. ***
  124. * val_delim
  125. ***
  126.  
  127. function val_delim
  128.    parameters _char
  129.  
  130.    _char = ltrim( trim( _char ) )        && trim it in case parseline() didn't
  131.  
  132.    if _char == "*"                       && asterik passed; delim = space
  133.       retu space( 1 )
  134.    endif
  135.  
  136.    if "" = _char                         && null passed; delim = number sign
  137.       retu "#"
  138.    endif
  139.  
  140.    private _delm_ok, _delm_msg
  141.    _delm_ok  = .F.
  142.    _delm_msg = ""
  143.  
  144.    do case
  145.    case len( _char ) > 1                 && only one alternate delim allowed
  146.       _delm_msg = "Must not exceed one character in length"
  147.    case asc( _char ) < 32 .OR. asc( _char ) > 127 && no special ASCII codes! 
  148.       _delm_msg = "Must not have a low (< 32) or high (> 127) ASCII value"
  149.    case isalpha( _char )                 && no letters allowed!
  150.       _delm_msg = "Must not be an alphabetic character"
  151.    case _char == "0" .OR. val( _char ) > 0  && no numbers, either!
  152.       _delm_msg = "Must not be a numeric character"
  153.    otherwise
  154.       _delm_ok = .T.                     && this char will do just fine!
  155.    endcase
  156.  
  157.    if !_delm_ok                          && bad delim char
  158.       ? "Invalid alternate delimiter " + _char
  159.       ? _delm_msg
  160.       ? "DOS Errorlevel: 1"
  161.       errorlevel( 1 )
  162.       quit
  163.    endif
  164.  
  165. return _char
  166.  
  167.  
  168.  
  169. ***
  170. * scanlines
  171. ***
  172.  
  173. function scanlines
  174.    parameters bufr_str
  175.  
  176.    private chrs_left, lf_pos, text_line, linefeed, crlf, detail, alt_delm, ;
  177.            dbfname, ntxname, ntx_key, _pos
  178.  
  179.    linefeed  = chr( 10 )
  180.    crlf      = chr( 13 ) + chr( 10 )
  181.    chrs_left = len( bufr_str )
  182.  
  183.    do while chrs_left != 0
  184.  
  185.       lf_pos = at( linefeed, bufr_str )  && line feed is end of ASCII line
  186.  
  187.       if lf_pos > 0                      && line feed found?
  188.          text_line = substr( bufr_str, 1, lf_pos )   && copy text from buffer
  189.          detail = strtran( text_line, crlf, "" )     && strip off CR/LF pair
  190.          dbfname = parseline( @detail, delm )        && break off 1st token
  191.  
  192.          do case             && check for dbfname, comment, or blank line
  193.          case "" = dbfname
  194.             *** blank line - skip it and keep pushin'! *** 
  195.          case "*" = substr( dbfname, 1, 1 )   && comment line found?
  196.             *** comment line - if delimiter change, validate it! *** 
  197.             _pos = at( "DELIMITER", upper( dbfname ) )
  198.             if _pos > 0
  199.                alt_delm = ltrim( trim( substr( dbfname, _pos + 9 ) ) )
  200.                delm = val_delim( alt_delm )
  201.             endif
  202.          otherwise
  203.             *** data line - cut it up and process the data! *** 
  204.             ntxname = parseline( @detail, delm )  && 2nd token is ntxname
  205.             ntx_key = parseline( @detail, delm )  && 3rd token is ntx_key
  206.             do_index( dbfname, ntxname, ntx_key ) && do the index thing!
  207.          endcase
  208.  
  209.          chrs_left = chrs_left - lf_pos  && deduct the chars done
  210.          bufr_str  = substr( bufr_str, lf_pos + 1, chrs_left ) && adj buffer
  211.       else
  212.          exit                            && else, we need more bananas (text)
  213.       endif
  214.  
  215.    enddo
  216.  
  217. return chrs_left
  218.  
  219.  
  220. ***
  221. * parseline
  222. ***
  223.  
  224. function parseline
  225.    parameter _line, _delm
  226.  
  227.    private delm_pos, piece
  228.  
  229.    delm_pos = at( _delm, _line )         && position of the next delimiter
  230.    piece = if( delm_pos > 0, substr( _line, 1, delm_pos - 1 ), _line )
  231.    _line = if( delm_pos > 0, substr( _line, delm_pos + 1 ), "" )
  232.    _line = ltrim( trim( _line ) )
  233.  
  234. return ltrim( trim( piece ) )
  235.  
  236.  
  237. ***
  238. * do_index
  239. ***
  240.  
  241. function do_index
  242.    parameters _dbfname, _ntxname, _ntx_key
  243.  
  244.    private msg_hdg
  245.  
  246.    _dbffile = val_fname( _dbfname, "DBF" )
  247.    _dbfname = val_fname( _dbfname )
  248.  
  249.    if !file( _dbffile )
  250.       ? _dbffile + " not found in current directory" 
  251.       ? "DOS Errorlevel: 2"
  252.       errorlevel( 2 )
  253.       quit
  254.    endif
  255.  
  256.    if !net_use( _dbfname, .T., 5 )       && open DBF for exclusive use
  257.       ? _dbffile + " unavailable for reindexing - in use by other user(s)"
  258.       ? "DOS Errorlevel: 32"
  259.       errorlevel( 32 )
  260.       quit
  261.    endif
  262.  
  263.    _ntxfile = val_fname( _ntxname, "NTX" )
  264.    _ntxname = val_fname( _ntxname )
  265.  
  266.    if file( _ntxfile )
  267.       copy file &_ntxfile to &_ntxname..NBK
  268.       delete file &_ntxfile
  269.       msg_hdg = "Replacing old index "
  270.    else
  271.       msg_hdg = "Creating new index "
  272.    endif
  273.  
  274.    _ntx_key = upper( ltrim( trim ( _ntx_key ) ) )
  275.    _test = type( "&_ntx_key" )           && test for valid key expression
  276.  
  277.    if _test  $ "CDN" .OR. _test == "UI"  && UI means an Extend.LIB func used
  278.       show_msg = msg_hdg + _ntxname + " for file " + _dbfname + "..."
  279.       ? show_msg
  280.       index on &_ntx_key to &_ntxname
  281.       use
  282.    else
  283.       ? "Index expression for " + _dbffile + " invalid: " + _ntx_key
  284.    endif
  285.  
  286. return .T.
  287.  
  288.  
  289. ***
  290. * val_fname
  291. ***
  292.  
  293. function val_fname
  294.    parameter _spec, _ext
  295.  
  296.    private period_pos, full_spec, result 
  297.  
  298.    _spec = upper( ltrim( trim( _spec ) ) )
  299.    full_spec = if( type( "_ext" ) == "C", .T., .F. )
  300.    period_pos = at( ".", _spec )
  301.  
  302.    if full_spec                          && .T. if fname+extension desired
  303.       result = if( period_pos > 0, _spec, _spec + "." + _ext )
  304.    else                                  && .F. if fname only desired
  305.       result = if( period_pos > 0, substr( _spec, 1, period_pos - 1 ), _spec )
  306.    endif
  307.  
  308. return result
  309.  
  310.  
  311. ***
  312. * net_use - from Nantucket Corp.
  313. ***
  314.  
  315. function net_use
  316.    parameters file, ex_use, wait
  317.  
  318.    private forever
  319.  
  320.    forever = ( wait = 0 )
  321.    do while ( forever .OR. wait > 0 )
  322.       if ex_use                          && exclusive
  323.          use &file exclusive
  324.       else
  325.          use &file                       && shared
  326.       endif
  327.  
  328.       if .not. neterr()                  && use succeeds
  329.          retu .T.
  330.       endif
  331.    
  332.       inkey( 1 )                         && wait 1 second
  333.       wait = wait - 1
  334.    enddo
  335.  
  336. return .F.                               && use fails
  337.  
  338.  
  339. ***
  340. * usage_help
  341. ***
  342.  
  343. function usage_help
  344.  
  345.    clear screen
  346.    ? "REINDEX - Clipper File Reindexing Utility, (c) 1990 Regmo Systems"
  347.    ? "Purpose: Create/Replace Clipper indexes via an index control file"
  348.    ? "Syntax : REINDEX [inputname[.ext]] [delim]"
  349.    ? "Where  : 'inputname' - name of text file containing the index data,"
  350.    ? "                       defaults to 'REINDEX.KEY' in current directory" 
  351.    ? "         '.ext'      - file extension; defaults to '.key'"
  352.    ? "         'delim'     - inline data delimiter; defaults to '#'"
  353.    ? "Sample Calls : reindex  or  reindex myfiles  or  reindex herdata.txt"
  354.    ? "Conventions  : Assumes 'inputname' is in current directory"
  355.    ?
  356.    ? "Inputname must be an ASCII text file, with a linefeed (hex 0A)"
  357.    ? "terminating each line of data, and an end-of-file (hex 1A) marker"
  358.    ? "as the last character. Each line of text must contain the following:"
  359.    ?
  360.    ? "         dbfname # ntxname # key_expression"
  361.    ?
  362.    ? "The number sign '#' is used to separate the data within each line."
  363.    ? "'dbfname' is the name of the DBF file to be indexed, 'ntxname' is"
  364.    ? "the name assigned to the index file, and 'key_expression' is any"
  365.    ? "valid Clipper index expression (Clipper functions and Extend.LIB"
  366.    ? "functions may be used). Lines beginning with an asterik '*' are"
  367.    ? "considered comments and are not processed; blank lines are ignored."
  368.    ?
  369.  
  370.    ? "Press any key for more help..."
  371.    inkey( 0 )
  372.  
  373.    ? "Delim must be a single character, with an ASCII value greater than 31"
  374.    ? "(hex 1F) and less than 128 (hex 80). Alphabetics, numerics, and the" 
  375.    ? "asterik cannot be used. The delimiter can be changed within the input"
  376.    ? "file by including it in a special comment line: '* DELIMITER delim'"
  377.    ? "This comment may appear multiple times in the text and the delimiter"
  378.    ? "will remain in effect until the next occurance. To assign a space as"
  379.    ? "the delimiter, set delim to '*'. If delim is left blank, the original"
  380.    ? "number sign delimiter '#' is restored. Here's a typical input file: "
  381.    ?
  382.    ? "* This is a comment; assume these lines are in myfiles.key"
  383.    ? "my1stfil # my1st1 # fld1"
  384.    ? "my1stfil # my1st2 # str( fld2, 2) + dtoc( datefld )"     
  385.    ? "* Change delimiter to '/' and use Extend.LIB functions in key:"
  386.    ? "* delimiter /"
  387.    ? "my2ndfil / my2nd1 / transform( numfld, '###' ) + strzero( othr_num )"
  388.    ? "* Lines are not case-sensitive; and blank lines are ignored..."              
  389.    ?
  390.    ? "* Change delimiter to a space and remove spaces in long expressions:"             
  391.    ? "* DELIMITER *"             
  392.    ? "my2ndfil my2nd2 substr(dtoc(inpdate),7,2)+str(invoice_no)"             
  393.    ? "* Change delimiter back to '#'"      
  394.    ? "* delimiter"              
  395.    ? "my3rdfil#my3rdndx#last_name + frst_name"              
  396.    ?
  397.  
  398.    quit
  399.  
  400. return .T.
  401.  
  402.  
  403. ***
  404. *
  405. * Buffered I/O for Clipper low-level file functions
  406. *
  407. *
  408. * First call BINIT with number of files to buffer, and size of each
  409. * buffer. Call BEND when finished to relase memory. BOPEN opens a
  410. * a file in buffered mode, BCLOSE closes it. BREAD reads from it.
  411. * BWRITE writes to it. Adapted from original code by Rick Spence.
  412. ***
  413.  
  414. ***
  415. * binit( num_handles, buffer_size )
  416. *
  417. * Initialize buffering system, return .T. for succcess, .F. for failure
  418. *
  419.  
  420. function binit
  421.  
  422.    parameters num_handles, buffer_size
  423.  
  424.    public buffers[ num_handles ]
  425.    public buff_size
  426.    public next_char[ num_handles ]
  427.    public num_in_buff[ num_handles ]
  428.    public more_to_read[ num_handles ]
  429.    public handles[ num_handles ]
  430.  
  431.    buff_size = buffer_size
  432.    afill( handles, 0 )
  433.  
  434. return .T.
  435.  
  436.  
  437. ***
  438. *   beof( handle )
  439. *
  440. *   Return end of file status for this file
  441. *
  442.  
  443. function beof
  444.  
  445.    parameters handle
  446.    private buff_no
  447.  
  448.    buff_no = ascan( handles, handle )
  449.  
  450. return !more_to_read[ buff_no ] .and. ;
  451.        next_char[ buff_no ] = num_in_buff[ buff_no ] + 1
  452.  
  453.  
  454. ***
  455. * bend()
  456. *
  457. * Terminate the buffering system.
  458. *
  459.  
  460. function bend
  461.  
  462.    release buffers
  463.    release buff_size
  464.    release next_char
  465.    release num_in_buff
  466.    release more_to_read
  467.    release handles
  468.  
  469. return .T.
  470.  
  471.  
  472. ***
  473. * bopen( file_spec, open_mode )
  474. *
  475. * Open file_spec with open_mode. Return file handle. -1 if error.
  476. *
  477.  
  478. function bopen
  479.  
  480.    parameters file_spec, open_mode
  481.    private handle, buff_no
  482.  
  483.    handle = fopen( file_spec, open_mode )
  484.  
  485.    if handle != ( -1 )
  486.       * allocate a buffer number for it ...
  487.       buff_no = ascan( handles, 0 )
  488.       if buff_no != 0
  489.          * set up structure
  490.          handles[ buff_no ] = handle
  491.          buffers[ buff_no ] = space( buff_size )
  492.          next_char[ buff_no ] = 1
  493.          num_in_buff[ buff_no ] = 0
  494.          more_to_read[ buff_no ] = .T.
  495.       else
  496.          * no room for buffer, so close file and return -1
  497.          fclose( handle )
  498.          handle = -1
  499.       endif
  500.    endif
  501.  
  502. return handle
  503.  
  504.  
  505. ***
  506. * bclose( handle )
  507. *
  508. * Close handle.
  509. *
  510.  
  511. function bclose
  512.  
  513.    parameter handle
  514.    private buff_no
  515.  
  516.    buff_no = ascan( handles, handle )
  517.  
  518.    buffers[ buff_no ] = ""
  519.    handles[ buff_no ] = 0
  520.    fclose( handle )
  521.  
  522. return .T.
  523.  
  524.  
  525. ***
  526. * bcreate( file_spec, open_mode )
  527. *
  528. * Create file_spec with open_mode. Return file handle. -1 if error.
  529. *
  530.  
  531. function bcreate
  532.  
  533.    parameters file_spec, open_mode
  534.    private handle, buff_no
  535.  
  536.    handle = fcreate( file_spec, open_mode )
  537.  
  538.    if handle != ( -1 )
  539.       * allocate a buffer number for it ...
  540.       buff_no = ascan( handles, 0 )
  541.       if buff_no != 0
  542.          * set up structure
  543.          handles[ buff_no ] = handle
  544.          buffers[ buff_no ] = space( buff_size )
  545.          next_char[ buff_no ] = 1
  546.          num_in_buff[ buff_no ] = 0
  547.          more_to_read[ buff_no ] = .T.
  548.       else
  549.          * no room for buffer, so close file and return -1
  550.          fclose( handle )
  551.          handle = -1
  552.       endif
  553.    endif
  554.  
  555. return handle
  556.  
  557.  
  558. ***
  559. * bread( handle, buffer, size )
  560. *
  561. * Buffered read from handle for size bytes.  Buffer must be passed by
  562. * reference.  Returns number of bytes read.
  563. *
  564.  
  565. function bread
  566.  
  567.    parameters handle, buffer, size
  568.    private remain, tbuffer, buff_no
  569.  
  570.    buff_no = ascan( handles, handle )
  571.    tbuffer = space( buff_size )
  572.  
  573.    * can the entire read be satisfied from the buffer ??
  574.    remain = num_in_buff[ buff_no ] - next_char[ buff_no ] + 1
  575.  
  576.    if remain >= size
  577.       * yes it can, so simply return it
  578.       buffer = substr( buffers[ buff_no ], next_char[ buff_no ], size )
  579.       next_char[ buff_no ] = next_char[ buff_no ] + size
  580.  
  581.    else
  582.       * no it can't, so get what is required from this buffer then
  583.       * refill repeatedly until bread is satisfied
  584.       if remain > 0
  585.          buffer = substr( buffers[ buff_no ], next_char[ buff_no ], remain )
  586.          size = size - remain
  587.          next_char[ buff_no ] = next_char[ buff_no ] + remain
  588.       else
  589.          buffer = ""
  590.       endif
  591.  
  592.       do while size > 0 .and. more_to_read[ buff_no ]
  593.          * refill buffer, or best we can ...
  594.          num_in_buff[ buff_no ] = fread( handle, @tbuffer, buff_size )
  595.          buffers[ buff_no ] = tbuffer
  596.          more_to_read[buff_no] = if(num_in_buff[buff_no] = buff_size,.T.,.F.)
  597.  
  598.          * can it now be satisfied from buffer ??
  599.          if size <= num_in_buff[ buff_no ]
  600.             * yes, so finish off ...
  601.             buffer = buffer + substr( buffers[ buff_no ], 1, size )
  602.             next_char[ buff_no ] = size + 1
  603.             size = 0
  604.          else
  605.             buffer = buffer + substr( buffers[ buff_no ], 1, ;
  606.             num_in_buff[ buff_no ] )
  607.             next_char[ buff_no ] = num_in_buff[ buff_no ] + 1
  608.             size = size - num_in_buff[ buff_no ]
  609.          endif
  610.       enddo
  611.    endif
  612.  
  613. return len( buffer )
  614.  
  615.  
  616. ***
  617. * bwrite( handle, buffer, size )
  618. *
  619. * Write to handle from buffer for size no. of bytes, 
  620. * returns no. of bytes written.
  621. *
  622. * NOTE: This function is for compatability within the buffered I/O
  623. *       function set. No special processing is performed and all parms
  624. *       are passed to FWRITE(), which returns the no. of bytes written.
  625. *
  626.  
  627. function bwrite
  628.  
  629.    parameters handle, buffer, size
  630.  
  631.    size = if( type( "size" ) = "n", size, len( buffer ) )
  632.  
  633. return fwrite( handle, buffer, size )
  634.  
  635.  
  636. ***
  637. * btell( handle )
  638. *
  639. * Returns the current file position within handle.
  640. *
  641. * NOTE: This function is for compatability within the buffered I/O
  642. *       function set. No special processing is performed and all parms are
  643. *       passed to FSEEK(), which returns the current position in the file.
  644. *
  645.  
  646. function btell
  647.  
  648.    parameters handle
  649.  
  650. return fseek( handle, 0, 1 ) 
  651.  
  652.  
  653. * EOF: REINDEX.PRG
  654.  
  655.  
  656.  
  657.